﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net.NetworkInformation;
using System.Net;
using System.Runtime.InteropServices;

namespace DevelopTool.Common
{
    /// <summary>
    /// 端口信息实体
    /// </summary>
    public class PortInfo
    {
        /// <summary>
        /// 连接类型 TCP或UDP
        /// </summary>
        public string Protocol { get; set; }

        /// <summary>
        /// 本地地址
        /// </summary>
        public string LocalAddress { get; set; }

        /// <summary>
        /// 本地端口
        /// </summary>
        public int LocalPort { get; set; }

        /// <summary>
        /// 远程地址
        /// </summary>
        public string RemoteAddress { get; set; }

        /// <summary>
        /// 远程端口
        /// </summary>
        public int RemotePort { get; set; }

        /// <summary>
        /// 状态
        /// </summary>
        public string State { get; set; }

        /// <summary>
        /// 进程ID
        /// </summary>
        public int ProcessId { get; set; }

        /// <summary>
        /// 启动时间
        /// </summary>
        public DateTime StartTime { get; set; }

        /// <summary>
        /// 窗口标题
        /// </summary>
        public string MainWindowTitle { get; set; }

        /// <summary>
        /// 进程名称
        /// </summary>
        public string ProcessName { get; set; }

        /// <summary>
        /// 进程路径
        /// </summary>
        public string ProcessPath { get; set; }
    }

    /// <summary>
    /// 端口帮助类
    /// </summary>
    public class PortHelper
    {
        /// <summary>
        /// 检查TCP端口是否被占用
        /// </summary>
        /// <param name="port">要检查的端口号</param>
        /// <returns>如果端口被占用，则返回 true，否则返回 false。</returns>
        public static bool IsTcpPortInUse(int port)
        {
            IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] listeners = properties.GetActiveTcpListeners();
            return listeners.Any(it => it.Port == port);
        }


        /// <summary>
        /// 检查UDP端口是否被占用
        /// </summary>
        /// <param name="port">要检查的端口号</param>
        /// <returns>如果端口被占用，则返回 true，否则返回 false。</returns>
        public static bool IsUdpPortInUse(int port)
        {
            IPGlobalProperties properties = IPGlobalProperties.GetIPGlobalProperties();
            IPEndPoint[] listeners = properties.GetActiveUdpListeners();
            return listeners.Any(it => it.Port == port);
        }


        /// <summary>
        /// 检查TCP端口是否被占用，如果被占用，那么就返回进程ID
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static int GetTCPPortInUseProcessId(int port)
        {
            var tcpConnections = GetAllTcpConnections();
            if (tcpConnections.Any(it => it.LocalPort == port))
            {

                return tcpConnections.First(it => it.LocalPort == port).owningPid;
            }
            else
            {
                return 0;
            }
        }

        /// <summary>
        /// 检查UDP端口是否被占用，如果被占用，那么就返回进程ID
        /// </summary>
        /// <param name="port"></param>
        /// <returns></returns>
        public static int GetUDPPortInUseProcessId(int port)
        {
            var udpConnections = GetAllUdpConnections();
            if (udpConnections.Any(it => it.LocalPort == port))
            {
                return udpConnections.First(it => it.LocalPort == port).owningPid;
            }
            else
            {
                return 0;
            }
        }


        /// <summary>
        /// 获取所有连接
        /// </summary>
        /// <returns></returns>
        public static List<PortInfo> GetAllConnections()
        {
            var portInfos = new List<PortInfo>();

            var tcpConnections = GetAllTcpConnections();
            var udpConnections = GetAllUdpConnections();

            foreach (var tcpConnection in tcpConnections)
            {
                try
                {
                    var process = WindowsHelper.GetProcessById(tcpConnection.owningPid);

                    portInfos.Add(new PortInfo
                    {
                        Protocol = "TCP",
                        LocalAddress = GetIpAddress(tcpConnection.localAddr),
                        LocalPort = tcpConnection.LocalPort,
                        RemoteAddress = GetIpAddress(tcpConnection.remoteAddr),
                        RemotePort = tcpConnection.RemotePort,
                        State = ((TCP_CONNECTION_STATE)tcpConnection.state).ToString(),
                        ProcessId = tcpConnection.owningPid,
                        MainWindowTitle = process?.MainWindowTitle,
                        ProcessName = process?.ProcessName,
                        ProcessPath = process?.MainModule?.FileName,
                        StartTime = process?.StartTime ?? DateTime.MinValue,
                    });
                }
                catch
                {

                }
            }

            foreach (var udpConnection in udpConnections)
            {
                try
                {
                    var process = WindowsHelper.GetProcessById(udpConnection.owningPid);

                    portInfos.Add(new PortInfo
                    {
                        Protocol = "UDP",
                        LocalAddress = GetIpAddress(udpConnection.localAddr),
                        LocalPort = udpConnection.LocalPort,
                        ProcessId = udpConnection.owningPid,
                        ProcessName = process?.ProcessName,
                        MainWindowTitle = process?.MainWindowTitle,
                        ProcessPath = process?.MainModule?.FileName,
                        StartTime = process?.StartTime ?? DateTime.MinValue,
                    });
                }
                catch
                {

                }


            }

            return portInfos;
        }


        [DllImport("Ws2_32.dll")]
        static extern ushort ntohs(ushort netshort);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedTcpTable(IntPtr pTcpTable, ref int dwOutBufLen, bool sort, int ipVersion, TCP_TABLE_TYPE tblClass, int reserved);

        [DllImport("iphlpapi.dll", SetLastError = true)]
        static extern uint GetExtendedUdpTable(IntPtr pUdpTable, ref int dwOutBufLen, bool sort, int ipVersion, UDP_TABLE_TYPE tblClass, int reserved);

        [DllImport("shell32.dll", ExactSpelling = true)]
        static extern void ILFree(IntPtr pidlList);

        [DllImport("shell32.dll", CharSet = CharSet.Unicode, ExactSpelling = true)]
        static extern IntPtr ILCreateFromPathW(string pszPath);

        [DllImport("shell32.dll", ExactSpelling = true)]
        static extern int SHOpenFolderAndSelectItems(IntPtr pidlList, uint cild, IntPtr children, uint dwFlags);

        [StructLayout(LayoutKind.Sequential)]
        private struct MIB_TCPROW_OWNER_PID
        {
            public uint state;
            public uint localAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public uint remoteAddr;
            public byte remotePort1;
            public byte remotePort2;
            public byte remotePort3;
            public byte remotePort4;
            public int owningPid;

            public ushort LocalPort
            {
                get
                {
                    return BitConverter.ToUInt16(new byte[2] { localPort2, localPort1 }, 0);
                }
            }

            public ushort RemotePort
            {
                get
                {
                    return BitConverter.ToUInt16(new byte[2] { remotePort2, remotePort1 }, 0);
                }
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MIB_TCPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_TCPROW_OWNER_PID table;
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MIB_UDPROW_OWNER_PID
        {
            public uint localAddr;
            public byte localPort1;
            public byte localPort2;
            public byte localPort3;
            public byte localPort4;
            public int owningPid;

            public ushort LocalPort
            {
                get
                {
                    return BitConverter.ToUInt16(new byte[2] { localPort2, localPort1 }, 0);
                }
            }
        }

        [StructLayout(LayoutKind.Sequential)]
        private struct MIB_UDPTABLE_OWNER_PID
        {
            public uint dwNumEntries;
            MIB_UDPROW_OWNER_PID table;
        }

        private static string GetIpAddress(long ipAddrs)
        {
            try
            {
                System.Net.IPAddress ipAddress = new System.Net.IPAddress(ipAddrs);
                return ipAddress.ToString();
            }
            catch { return ipAddrs.ToString(); }
        }

        private static ushort GetTcpPort(int tcpPort)
        {
            return ntohs((ushort)tcpPort);
        }

        private static MIB_TCPROW_OWNER_PID[] GetAllTcpConnections()
        {
            MIB_TCPROW_OWNER_PID[] tcpConnectionRows;
            int AF_INET = 2;    // IPv4
            int buffSize = 0;

            uint ret = GetExtendedTcpTable(IntPtr.Zero, ref buffSize, true, AF_INET, TCP_TABLE_TYPE.TCP_TABLE_OWNER_PID_ALL, 0);
            if (ret != 0 && ret != 122)
            {
                // throw new Exception("查询tcp表时出错, 错误代码：" + ret);
            }
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                ret = GetExtendedTcpTable(buffTable, ref buffSize, true, AF_INET, TCP_TABLE_TYPE.TCP_TABLE_OWNER_PID_ALL, 0);
                if (ret != 0)
                {
                    // throw new Exception("查询tcp表时出错, 错误代码：" + ret);
                }

                MIB_TCPTABLE_OWNER_PID table = (MIB_TCPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_TCPTABLE_OWNER_PID));
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(table.dwNumEntries));
                tcpConnectionRows = new MIB_TCPROW_OWNER_PID[table.dwNumEntries];

                for (int i = 0; i < table.dwNumEntries; i++)
                {
                    MIB_TCPROW_OWNER_PID tcpRow = (MIB_TCPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_TCPROW_OWNER_PID));
                    tcpConnectionRows[i] = tcpRow;
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(tcpRow));
                }
            }
            finally
            {
                Marshal.FreeHGlobal(buffTable);
            }
            return tcpConnectionRows;
        }

        private static MIB_UDPROW_OWNER_PID[] GetAllUdpConnections()
        {
            MIB_UDPROW_OWNER_PID[] udpConnectionRows;
            int AF_INET = 2;    // IPv4
            int buffSize = 0;

            uint ret = GetExtendedUdpTable(IntPtr.Zero, ref buffSize, true, AF_INET, UDP_TABLE_TYPE.UDP_TABLE_OWNER_PID, 0);
            if (ret != 0 && ret != 122)
            {
                // throw new Exception("查询udp表时出错, 错误代码：" + ret);
            }
            IntPtr buffTable = Marshal.AllocHGlobal(buffSize);

            try
            {
                ret = GetExtendedUdpTable(buffTable, ref buffSize, true, AF_INET, UDP_TABLE_TYPE.UDP_TABLE_OWNER_PID, 0);
                if (ret != 0)
                {
                    // throw new Exception("查询udp表时出错, 错误代码：" + ret);
                }

                MIB_UDPTABLE_OWNER_PID table = (MIB_UDPTABLE_OWNER_PID)Marshal.PtrToStructure(buffTable, typeof(MIB_UDPTABLE_OWNER_PID));
                IntPtr rowPtr = (IntPtr)((long)buffTable + Marshal.SizeOf(table.dwNumEntries));
                udpConnectionRows = new MIB_UDPROW_OWNER_PID[table.dwNumEntries];

                for (int i = 0; i < table.dwNumEntries; i++)
                {
                    MIB_UDPROW_OWNER_PID udpRow = (MIB_UDPROW_OWNER_PID)Marshal.PtrToStructure(rowPtr, typeof(MIB_UDPROW_OWNER_PID));
                    udpConnectionRows[i] = udpRow;
                    rowPtr = (IntPtr)((long)rowPtr + Marshal.SizeOf(udpRow));
                }
            }
            finally
            {
                Marshal.FreeHGlobal(buffTable);
            }
            return udpConnectionRows;
        }

    }

    public enum TCP_TABLE_TYPE : int
    {
        TCP_TABLE_BASIC_LISTENER,
        TCP_TABLE_BASIC_CONNECTIONS,
        TCP_TABLE_BASIC_ALL,
        TCP_TABLE_OWNER_PID_LISTENER,
        TCP_TABLE_OWNER_PID_CONNECTIONS,
        TCP_TABLE_OWNER_PID_ALL,
        TCP_TABLE_OWNER_MODULE_LISTENER,
        TCP_TABLE_OWNER_MODULE_CONNECTIONS,
        TCP_TABLE_OWNER_MODULE_ALL
    }

    public enum UDP_TABLE_TYPE : int
    {
        UDP_TABLE_BASIC,
        UDP_TABLE_OWNER_PID
    }

    public enum TCP_CONNECTION_STATE : int
    {
        CLOSED = 1,
        LISTENING,
        SYN_SENT,
        SYN_RCVD,
        ESTABLISHED,
        FIN_WAIT_1,
        FIN_WAIT_2,
        CLOSE_WAIT,
        CLOSING,
        LAST_ACK,
        TIME_WAIT,
        DELETE_TCP
    }
}
